#include "p3.h"
using namespace std;
string file;
ofstream output;
bool tiempo = false;

ostream& operator<<(ostream& os, const pair<int,int>& p){
	os << "<" << p.first << "," << p.second << ">";
	return os;
}

template<class T> ostream& operator<<(ostream& os, const vector<T>& v){
	os << "<";
	for(int i=0; i< v.size();i++){
		os << v[i];
		if(i != v.size()-1){
			os << ",";
		}
	}
	os << ">";
	return os;
}

string get_file_contents(const char *filename){
	ifstream in(filename, std::ios::in | std::ios::binary);
	if (in){
		string contents;
		in.seekg(0, std::ios::end);
		contents.resize(in.tellg());
		in.seekg(0, std::ios::beg);
		in.read(&contents[0], contents.size());
		in.close();
		return(contents);
	}
	perror("Error:");
	return 0;
}

int main(int argc, char *argv[]) {

	if(argc ==1){
		cout << "Usando p3.in y p3.out" << endl;
		file = get_file_contents("p3.in");
		output.open("p3.out");
		if(output.fail()){
			cerr << "Error al abrir archivo de salida" << endl;
		}
	}

	else if(argc ==3){
		cout << "Usando " << argv[1] << " y " << argv[2] << endl;
		file = get_file_contents(argv[1]);
		output.open(argv[2]);
		if(output.fail()){
			cerr << "Error al abrir archivo de salida" << endl;
		}
	}

	else if(argc ==2 && string (argv[1]) == "time"){
		cout << "Calculando tiempos usando p3.in y p3.out con " << ITER << " iteraciones" << endl;
		tiempo = true;
		file = get_file_contents("p3.in");
		output.open("p3.out");
		if(output.fail()){
			cerr << "Error al abrir archivo de salida" << endl;
		}
	}

	else{
		cout << "Modo de uso: \"p3 input output\" o \"p3 time\" para benchmark" << endl;
		return 0;
    }

	istringstream input;
	input.str(file);

	while(!input.eof()){
		if(input.peek() == '#'){
			break;
		}
//		int k = input.peek()-'0';
		int k;
		input >> k;
		int base;
		int altura;
		vector<int> vx(k);
		vector<int> vy(k);
		vector<pair<int,int> > puntos(k);
		cargar(input,k,base,altura,vx,vy,puntos);
		if(tiempo){
			uint64_t t;
			uint64_t res = 0;
			for(int i=0;i<ITER;i++){
				t=rdtsc();
				resolver(k,base,altura,puntos);
				t = rdtsc() -t;
				res = res + t;
			}
		res = res/ITER;
		cout << "For k= " << k << " process took: "<< res << " ticks" << endl;
		}
		xyp sol = resolver(k,base,altura,puntos);
		salida(output,sol);
	}
	output.close();
	return 0;
}

void cargar(istringstream& is, int& k, int& base, int& altura, vector<int>& vx, vector<int>& vy, vector<pair<int,int> >& puntos){
	string l1;
	string l2;
	getline(is,l1);
	getline(is,l2);
	istringstream ssl1(l1);
	istringstream ssl2(l2);
//	ssl1 >> k;
	ssl1 >> base;
	ssl1 >> altura;
	for(int i=0; i < k; i++){
		ssl2 >> vx[i];
		ssl2 >> vy[i];
		puntos[i].first = vx[i];
		puntos[i].second = vy[i];
	}
}
void mergeX(vector<pair<int,int> >& arr, int m, int l, int n){
	vector<pair<int,int> > res;
	int a,s;
	a= m;
	s= l+1;

	int pivot = l;
	while(a<=pivot && s<=n){
		if (arr[a].first > arr[s].first){
			res.push_back(arr[s]);
			s++;
		}else{
			res.push_back(arr[a]);
			a++;
		}
	}

		for(int i= a ; i<=pivot ;i++){
			res.push_back(arr[i]);}

		for(int i= s ; i<=n ;i++){
			res.push_back(arr[i]);}
    int p = 0;
	for(int k = m; k<=n ; k++){
	    arr[k]=res[p];
	    p++;

	}

}




void mergesortX(vector<pair<int,int> >& a,int m, int n){
        if( n-m == 1 && a[m].first>a[n].first ){
                pair<int,int> temp = a[m];
                a[m] =  a[n];
                a[n] =  temp;

        }else if(n-m>1){
		int l = (n-m)/2;
		mergesortX(a,m,m+l);
		mergesortX(a,m+l+1,n);
		mergeX( a,m,m+l,n);


	}
}




xyp resolver(int& k, int& base, int& altura,  vector<pair<int,int> >& puntos){
	xyp res;
	res.second = 0;

	mergesortX(puntos,0,k-1);//ordeno los puntos por la primer coordenada

	int minX = 0;
	int minY = 0;
	int max = 0;

	for(int i = 0; i<k; i++){ //lamemos a este elemento A
		int tmpMinX = 0;
		int tmpMinY = 0;


		for(int j = i; j<k; j++){ // Tomo otro elemento B que este en el cuadrado de A sumando o restando altura
			if ( puntos[i].first + base >= puntos[j].first &&
			   puntos[i].second - altura <= puntos[j].second    ){

				//Agarro 2 puntos en el plano si estan a distancia B me quedo con el menor X y el menor Y
				if(puntos[i].first > puntos[j].first){
					tmpMinX = puntos[j].first;
				}else{
					tmpMinX = puntos[i].first;
				}

				if(puntos[i].second > puntos[j].second){
					tmpMinY = puntos[j].second;
				}else{
					tmpMinY = puntos[i].second;
				}
				int tmpMaxUp = 0;

				for(int m = 0; m<k;m++){ //Me fijo entre A y B que elementos comparten.
						if(tmpMinX <= puntos[m].first && tmpMinX + base >= puntos[m].first){

							if  ( tmpMinY + altura >= puntos[m].second && puntos[m].second >= tmpMinY ){
								tmpMaxUp++;

							}

						}
				}

					if(max<tmpMaxUp){
						max = tmpMaxUp;
						minX = tmpMinX;
						minY = tmpMinY;
					}

			}


	}
	}
	res.second = max;
	res.first.first = minX;
	res.first.second = minY;
	return res;


}
void salida(ostream& out, xyp& sol){
	out << sol.first.first << " " << sol.first.second << " " << sol.second << endl;
}
